#pragma once
#include<mutex>

namespace myptr
{
	template<class T>
	class auto_ptr {
	private:
		T* _ptr;
	public:
		auto_ptr(T* ptr)
			:_ptr(ptr)
		{}

		~auto_ptr() { delete _ptr; }

		auto_ptr(auto_ptr<T>& sp)
			:_ptr(sp._ptr)
		{
			sp._ptr = nullptr;
		}

		T& operator*() { return *_ptr; }
		T* operator->() { return _ptr; }
	};

	template<class T>
	class default_delete {
	public:
		void operator()() { }
	};

	template<class T>
	class unique_ptr {
	private:
		T* _ptr;
	public:
		unique_ptr(T* ptr)
			:_ptr(ptr)
		{}
		~unique_ptr() { delete _ptr; }

		unique_ptr(const unique_ptr<T>& up) = delete;
		unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;

		T& operator*() { return *_ptr; }
		T* operator->() { return _ptr; }
	};

	template<class T>
	class shared_ptr {
	private:
		T* _ptr;
		int* _pcount;
		std::mutex* _pmtx;

	public:
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			,_pcount(new int(1))
			,_pmtx(new std::mutex)
		{}
		~shared_ptr() { Release(); }

		shared_ptr(const shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pcount(sp._pcount)
			,_pmtx(sp._pmtx)
		{
			{
				_pmtx->lock();
				++(*_pcount);
				_pmtx->unlock();
			}
		}
		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			if (_ptr == sp._ptr) return *this;
			Release();

			_pcount = sp._pcount;
			_ptr = sp._ptr;
			_pmtx = sp._pmtx;

			{
				_pmtx->lock();
				++(*_pcount);
				_pmtx->unlock();
			}
			return *this;
		}

		void Release()
		{
			bool flag = false;
			{
				_pmtx->lock();
				if (--(*_pcount) == 0)
				{
					 delete _ptr;
					delete _pcount;
					flag = true;
				}
				_pmtx->unlock();
			}
			if (flag) delete _pmtx;
		}

		int use_count() const { return *_pcount; }
		T* get() const { return _ptr; }
		T& operator*() { return *_ptr; }
		T* operator->() { return _ptr; }
	};

	template<class T>
	class weak_ptr
	{
	private:
		T* _ptr;
	public:
		weak_ptr()
			:_ptr(nullptr)
		{}


		weak_ptr(const weak_ptr<T>& wp)
			:_ptr(wp._ptr)
		{}

		weak_ptr(const shared_ptr<T>& sp)
			:_ptr(sp.get())
		{}

		weak_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			_ptr = sp.get();
			return *this;
		}

		T& operator*() { return *_ptr; }
		T* operator->() { return _ptr; }
	};
}
